1   /*
2    * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 6204469
27   * @summary Test that Open MBean attributes and parameters cannot have
28   * illegal constraints like min greater than max
29   * @author Eamonn McManus
30   * @run clean BadConstraintTest
31   * @run build BadConstraintTest
32   * @run main BadConstraintTest
33   */
34  
35  import java.io.*;
36  import java.lang.reflect.*;
37  import java.util.*;
38  import javax.management.*;
39  import javax.management.openmbean.*;
40  
41  public class BadConstraintTest {
42      private static String failure;
43  
44      public static void main(String[] args) throws Exception {
45          genericTests();
46          descriptorTests();
47  
48          if (failure == null)
49              System.out.println("Test passed");
50          else
51              throw new Exception("TEST FAILED: " + failure);
52      }
53  
54      private static void genericTests() throws Exception {
55          for (Object[] test : tests) {
56              if (test.length != 5) {
57                  throw new Exception("Test element has wrong length: " +
58                                      toString(test));
59              }
60  
61              OpenType<?> openType = (OpenType<?>) test[0];
62              Object defaultValue = test[1];
63              Comparable<?> minValue = (Comparable<?>) test[2];
64              Comparable<?> maxValue = (Comparable<?>) test[3];
65              Object[] legalValues = (Object[]) test[4];
66  
67              System.out.println("test: openType=" + openType +
68                                 "; defaultValue=" + defaultValue +
69                                 "; minValue=" + minValue +
70                                 "; maxValue=" + maxValue +
71                                 "; legalValues=" + toString(legalValues));
72  
73              genericTest(openType, defaultValue, minValue, maxValue,
74                          legalValues);
75          }
76      }
77  
78      private static void descriptorTests() throws Exception {
79          for (Object[][] test : descriptorTests) {
80              if (test.length != 2) {
81                  throw new Exception("Test element has wrong length: " +
82                                      toString(test));
83              }
84  
85              if (test[0].length != 1) {
86                  throw new Exception("Test element should have one OpenType: " +
87                                      toString(test[0]));
88              }
89  
90              OpenType<?> openType = (OpenType<?>) test[0][0];
91              Descriptor d = descriptor(test[1]);
92  
93              System.out.println("test: openType=" + openType +
94                                 "; descriptor=" + d);
95  
96              descriptorTest(openType, d);
97          }
98      }
99  
100     /* Tests that apply to both the Descriptor and the non-Descriptor
101        constructors.  We invoke the non-Descriptor constructors by
102        reflection, then we make the corresponding Descriptor and call
103        the descriptorTest with it.  */
104     private static void genericTest(OpenType<?> openType,
105                                     Object defaultValue,
106                                     Comparable<?> minValue,
107                                     Comparable<?> maxValue,
108                                     Object[] legalValues)
109             throws Exception {
110 
111         if (minValue == null && maxValue == null && legalValues == null) {
112             if (defaultValue == null)
113                 throw new Exception("What am I testing?");
114             Class[] params1 = new Class[] {
115                 String.class, String.class, OpenType.class,
116                 boolean.class, boolean.class, boolean.class,
117                 Object.class
118             };
119             Constructor<OpenMBeanAttributeInfoSupport> c1 =
120                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
121             Class[] params2 = new Class[] {
122                 String.class, String.class, OpenType.class,
123                 Object.class
124             };
125             Constructor<OpenMBeanParameterInfoSupport> c2 =
126                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
127             ode(c1, "name", "descr", openType, true, true, false, defaultValue);
128             ode(c2, "name", "descr", openType, defaultValue);
129             descriptorTest(openType,
130                            descriptor("defaultValue", defaultValue));
131             descriptorTest(openType,
132                            descriptor("defaultValue", string(defaultValue)));
133         }
134 
135         if (legalValues == null) {
136             Class[] params1 = new Class[] {
137                 String.class, String.class, OpenType.class,
138                 boolean.class, boolean.class, boolean.class,
139                 Object.class, Comparable.class, Comparable.class
140             };
141             Constructor<OpenMBeanAttributeInfoSupport> c1 =
142                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
143             Class[] params2 = new Class[] {
144                 String.class, String.class, OpenType.class,
145                 Object.class, Comparable.class, Comparable.class
146             };
147             Constructor<OpenMBeanParameterInfoSupport> c2 =
148                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
149             ode(c1, "name", "descr", openType, true, true, false, defaultValue,
150                 minValue, maxValue);
151             ode(c2, "name", "descr", openType, defaultValue,
152                 minValue, maxValue);
153             descriptorTest(openType,
154                            descriptor("defaultValue", defaultValue,
155                                       "minValue", minValue,
156                                       "maxValue", maxValue));
157             descriptorTest(openType,
158                            descriptor("defaultValue", string(defaultValue),
159                                       "minValue", string(minValue),
160                                       "maxValue", string(maxValue)));
161         }
162 
163         if (legalValues != null) {
164             Class[] params1 = new Class[] {
165                 String.class, String.class, OpenType.class,
166                 boolean.class, boolean.class, boolean.class,
167                 Object.class, Object[].class
168             };
169             Constructor<OpenMBeanAttributeInfoSupport> c1 =
170                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
171             Class[] params2 = new Class[] {
172                 String.class, String.class, OpenType.class,
173                 Object.class, Object[].class
174             };
175             Constructor<OpenMBeanParameterInfoSupport> c2 =
176                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
177             ode(c1, "name", "descr", openType, true, true, false, defaultValue,
178                 legalValues);
179             ode(c2, "name", "descr", openType, defaultValue,
180                 legalValues);
181             descriptorTest(openType,
182                            descriptor("defaultValue", defaultValue,
183                                       "legalValues", legalValues));
184             descriptorTest(openType,
185                            descriptor("defaultValue", defaultValue,
186                                       "legalValues", arraySet(legalValues)));
187             Set<String> strings = new HashSet<String>();
188             for (Object x : legalValues)
189                 strings.add(x.toString());
190             descriptorTest(openType,
191                            descriptor("defaultValue", defaultValue,
192                                       "legalValues", strings));
193             descriptorTest(openType,
194                            descriptor("defaultValue", defaultValue,
195                                       "legalValues",
196                                       strings.toArray(new String[0])));
197         }
198     }
199 
200     private static void descriptorTest(OpenType<?> openType, Descriptor d)
201             throws Exception {
202         Class[] params1 = new Class[] {
203             String.class, String.class, OpenType.class,
204             boolean.class, boolean.class, boolean.class,
205             Descriptor.class
206         };
207         Constructor<OpenMBeanAttributeInfoSupport> c1 =
208             OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
209         Class[] params2 = new Class[] {
210             String.class, String.class, OpenType.class,
211             Descriptor.class
212         };
213         Constructor<OpenMBeanParameterInfoSupport> c2 =
214             OpenMBeanParameterInfoSupport.class.getConstructor(params2);
215         iae(c1, "name", "descr", openType, true, true, false, d);
216         iae(c2, "name", "descr", openType, d);
217     }
218 
219     /* Check that the given constructor invocation gets an
220        IllegalArgumentException. */
221     private static void iae(Constructor<?> con, Object... params) {
222         checkException(IllegalArgumentException.class, con, params);
223     }
224 
225     /* Check that the given constructor invocation gets an
226        OpenDataException.  */
227     private static void ode(Constructor<?> con, Object... params) {
228         checkException(OpenDataException.class, con, params);
229     }
230 
231     private static void checkException(Class<? extends Exception> exc,
232                                        Constructor<?> con, Object[] params) {
233         try {
234             con.newInstance(params);
235             fail("Constructor succeeded but should have got " + exc.getName() +
236                  " with params " + Arrays.deepToString(params));
237         } catch (InvocationTargetException e) {
238             Throwable cause = e.getCause();
239             if (exc.isInstance(cause))
240                 return;
241             StringWriter sw = new StringWriter();
242             PrintWriter pw = new PrintWriter(sw);
243             cause.printStackTrace(pw);
244             pw.close();
245             fail("Constructor should have got " + exc.getName() +
246                  " with params " + Arrays.deepToString(params) + ": " + sw);
247         } catch (Exception e) {
248             throw new IllegalArgumentException("Reflection failed", e);
249         }
250     }
251 
252     private static void fail(String why) {
253         System.out.println("FAILED: " + why);
254         failure = why;
255     }
256 
257     private static Descriptor descriptor(Object... entries) {
258         if (entries.length % 2 != 0)
259             throw new RuntimeException("Odd length descriptor entries");
260         String[] names = new String[entries.length / 2];
261         Object[] values = new Object[entries.length / 2];
262         for (int i = 0; i < entries.length; i += 2) {
263             names[i / 2] = (String) entries[i];
264             values[i / 2] = entries[i + 1];
265         }
266         return new ImmutableDescriptor(names, values);
267     }
268 
269     private static <T> Set<T> arraySet(T[] array) {
270         return new HashSet<T>(Arrays.asList(array));
271     }
272 
273     private static String toString(Object x) {
274         if (x == null)
275             return "null";
276         else if (x.getClass().isArray()) {
277             StringBuilder sb = new StringBuilder("[");
278             int len = Array.getLength(x);
279             for (int i = 0; i < len; i++) {
280                 if (i > 0)
281                     sb.append(", ");
282                 sb.append(toString(Array.get(x, i)));
283             }
284             sb.append("]");
285             return sb.toString();
286         } else
287             return x.toString();
288     }
289 
290     private static String string(Object x) {
291         if (x == null)
292             return null;
293         return toString(x);
294     }
295 
296     private static final OpenType<?>
297         ostring = SimpleType.STRING,
298         oint = SimpleType.INTEGER,
299         obool = SimpleType.BOOLEAN,
300         olong = SimpleType.LONG,
301         obyte = SimpleType.BYTE,
302         ofloat = SimpleType.FLOAT,
303         odouble = SimpleType.DOUBLE,
304         ostringarray, ostringarray2;
305     private static final CompositeType ocomposite;
306     private static final CompositeData compositeData, compositeData2;
307     static {
308         try {
309             ostringarray = new ArrayType<String[]>(1, ostring);
310             ostringarray2 = new ArrayType<String[][]>(2, ostring);
311             ocomposite =
312                 new CompositeType("name", "descr",
313                                   new String[] {"s", "i"},
314                                   new String[] {"sdesc", "idesc"},
315                                   new OpenType[] {ostring, oint});
316             compositeData =
317                 new CompositeDataSupport(ocomposite,
318                                          new String[] {"s", "i"},
319                                          new Object[] {"foo", 23});
320             compositeData2 =
321                 new CompositeDataSupport(ocomposite,
322                                          new String[] {"s", "i"},
323                                          new Object[] {"bar", -23});
324         } catch (OpenDataException e) { // damn checked exceptions...
325             throw new IllegalArgumentException(e.toString(), e);
326         }
327     }
328 
329     private static final Descriptor
330         nullD = null,
331         emptyD = ImmutableDescriptor.EMPTY_DESCRIPTOR;
332 
333     /* Each element of this array contains five Objects:
334        - OpenType;
335        - defaultValue;
336        - minValue;
337        - maxValue;
338        - legalValues.  The objects are used to construct tests cases
339        where all possible constructors that make sense for that
340        combination of values are invoked, and all are checked to ensure
341        that they throw the right exception.  */
342     private static final Object[][] tests = {
343 
344         // Values must be of appropriate type
345 
346         {oint, "oops", null, null, null},
347         {oint, Long.MAX_VALUE, null, null, null},
348         {oint, null, "oops", null, null},
349         {oint, "oops", 3, null, null},
350         {oint, 3, "oops", null, null},
351         {oint, null, null, "oops", null},
352         {oint, null, 3, "oops", null},
353         {oint, null, 3, false, null},
354         {oint, null, null, null, new String[] {"x"}},
355         {oint, null, null, null, new Object[] {"x"}},
356         {oint, null, null, null, new Object[] {3, "x"}},
357 
358         // If defaultValue is present then it must satisfy the constraints
359         // defined by legalValues, minValue, or maxValue when any of
360         // these is also present
361 
362         {oint, 3, 4, null, null},
363         {oint, 3, null, 2, null},
364         {oint, 3, null, null, new Integer[] {2, 4}},
365 
366         // If minValue and maxValue are both present then minValue must
367         // not be greater than maxValue
368 
369         {ostring, null, "z", "a", null},
370         {oint, null, 3, 2, null},
371 
372         // We don't support default values or legal sets for arrays (yet)
373 
374         {ostringarray, new String[] {"x"}, null, null, null},
375         {ostringarray, null, null, null, new String[][]{new String[] {"x"}}},
376     };
377 
378     /* The tests here can only be expressed via Descriptors because an
379        attempt to invoke one of the non-Descriptor constructors with
380        the implied parameters would not compile (or would fail at the
381        reflection stage when reflection is being used).
382 
383        Each element of this array contains two subarrays.  The first
384        is an array of OpenTypes that must contain exactly one element.
385        The second is an array of alternating field names and field
386        values that will be used to construct a Descriptor that is supposed
387        to fail when given to an OpenMBean*Info constructor with the given
388        OpenType.  */
389     private static final Object[][][] descriptorTests = {
390 
391         // Values must be of appropriate type
392 
393         {{oint},
394          {"minValue", 25L}},
395 
396         {{oint},
397          {"minValue", new Object()}}, // not even Comparable
398         {{oint},
399          {"maxValue", new Object()}}, // not even Comparable
400         {{oint},
401          {"defaultValue", 3,
402           "minValue", new Object()}},
403         {{oint},
404          {"defaultValue", 3,
405           "maxValue", new Object()}},
406 
407         {{oint},
408          {"legalValues", new int[] {3}}}, // should be new Integer[] to work
409         {{oint},
410          {"legalValues", 3}},
411 
412         // If legalValues is present then neither minValue nor maxValue
413         // must be present
414 
415         {{oint},
416          {"minValue", 3, "legalValues", new Integer[] {3, 4}}},
417         {{oint},
418          {"maxValue", 3, "legalValues", new Integer[] {2, 3}}},
419         {{oint},
420          {"defaultValue", 3, "minValue", 3, "legalValues", new Integer[] {3}}},
421 
422         // We don't support min or max arrays (what would they mean?)
423 
424         {{ostringarray},
425          {"minValue", new String[] {"x"}}},
426         {{ostringarray},
427          {"maxValue", new String[] {"x"}}},
428 
429     };
430 }